package cn.lyx.xiaohoshu.auth.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.lyx.framework.common.enums.DeleteEnum;
import cn.lyx.framework.common.enums.StatusEnum;
import cn.lyx.framework.common.exception.BizException;
import cn.lyx.framework.common.response.Response;
import cn.lyx.framework.common.util.JsonUtils;
import cn.lyx.framework.context.holder.LoginUserContextHolder;
import cn.lyx.xiaohoshu.auth.config.PasswordEncoderConfig;
import cn.lyx.xiaohoshu.auth.contants.RedisKeyConstants;
import cn.lyx.xiaohoshu.auth.contants.RoleConstants;
import cn.lyx.xiaohoshu.auth.domain.dataobject.RoleDO;
import cn.lyx.xiaohoshu.auth.domain.dataobject.UserDO;
import cn.lyx.xiaohoshu.auth.domain.dataobject.UserRoleRelDO;
import cn.lyx.xiaohoshu.auth.domain.mapper.RoleDOMapper;
import cn.lyx.xiaohoshu.auth.domain.mapper.UserDOMapper;
import cn.lyx.xiaohoshu.auth.domain.mapper.UserRoleRelDOMapper;
import cn.lyx.xiaohoshu.auth.enums.LoginTypeEnum;
import cn.lyx.xiaohoshu.auth.enums.ResponseCodeEnum;
import cn.lyx.xiaohoshu.auth.model.vo.user.UpdateUserPasswordReqVO;
import cn.lyx.xiaohoshu.auth.model.vo.user.UserLoginReqVO;
import cn.lyx.xiaohoshu.auth.service.UserService;
import com.google.common.base.Preconditions;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Author by lyx
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Resource
    private UserDOMapper userDOMapper;
    @Resource
    UserRoleRelDOMapper userRoleRelDOMapper;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private RoleDOMapper roleDOMapper;
    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public Response<String> loginAndRegister(UserLoginReqVO userLoginReqVO) {
        String phone = userLoginReqVO.getPhone();
        Integer type = userLoginReqVO.getType();

        LoginTypeEnum loginTypeEnum = LoginTypeEnum.valueOf(type);

        Long userId = null;

        switch (loginTypeEnum) {
            case VERIFICATION_CODE -> {

                // 判断验证码是否为空
                String code = userLoginReqVO.getCode();

                Preconditions.checkArgument(StringUtils.isNoneBlank(code), "验证码不能为空！");

                // 构建 redis key
                String redisKey = RedisKeyConstants.buildVerificationCodeKey(phone);

                String sentVerificationCode = (String) redisTemplate.opsForValue().get(redisKey);

                // 判断验证码是否正确
                if (!StringUtils.equals(code, sentVerificationCode)) {
                    throw new BizException(ResponseCodeEnum.VERIFICATION_CODE_ERROR);
                }

                // 通过手机号查询记录
                UserDO userDO = userDOMapper.selectByPhone(phone);

                log.info("==> 用户是否注册, phone: {}, userDO: {}", phone, JsonUtils.toJsonString(userDO));

                // 判断是否注册
                if (Objects.isNull(userDO)) {
                    // 自动注册
                    userId = registerUser(phone);
                } else {
                    userId = userDO.getId();
                }
                break;
            }
            case PASSWORD -> {
                String password = userLoginReqVO.getPassword();
                UserDO userDO1 = userDOMapper.selectByPhone(phone);
                if (Objects.isNull(userDO1)) {
                    throw new BizException(ResponseCodeEnum.USER_NOT_FOUND);
                }
                // 存在 则拿到该用户密码
                String userDOPassword = userDO1.getPassword();
                if (!passwordEncoder.matches(password, userDOPassword)) {
                    throw new BizException(ResponseCodeEnum.PHONE_OR_PASSWORD_ERROR);
                }
                userId = userDO1.getId();
                break;
            }
            default -> {
                break;
            }
        }

        StpUtil.login(userId);

        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

        return Response.success(tokenInfo.tokenValue);
    }

    @Override
    public Response<?> logout() {
        Long userId = LoginUserContextHolder.getUserId();
        StpUtil.logout(userId);
        return Response.success();
    }

    @Override
    public Response<?> updateUserPassword(UpdateUserPasswordReqVO updateUserPasswordReqVO) {
        String newPassword = updateUserPasswordReqVO.getNewPassword();
        String encode = passwordEncoder.encode(newPassword);
        Long userId = LoginUserContextHolder.getUserId();
        UserDO userDO = UserDO.builder().id(userId).password(encode).updateTime(LocalDateTime.now()).build();
        userDOMapper.updateByPrimaryKeySelective(userDO);
        return Response.success();
    }

    private Long registerUser(String phone) {
        return transactionTemplate.execute(status -> {
            // 操作 Redis , 获取全局自增的小哈书 ID；
            Long xiaohoshuId = redisTemplate.opsForValue().increment(RedisKeyConstants.XIAOHOSHU_ID_GENERATOR_KEY);

            // 构建 UserDO 实体类，包括分配小红书 ID, 昵称等；
            UserDO userDO = UserDO.builder()
                    .phone(phone)
                    .xiaohashuId(String.valueOf(xiaohoshuId))
                    .nickname("小红薯" + xiaohoshuId)
                    .status(StatusEnum.ENABLE.getValue())
                    .isDeleted(DeleteEnum.NO.getValue())
                    .createTime(LocalDateTime.now())
                    .updateTime(LocalDateTime.now())
                    .build();

            // 插入用户数据
            userDOMapper.insert(userDO);

            // 并获取其主键 ID ;
            Long userID = userDO.getId();

            // 为用户分配默认角色
            UserRoleRelDO userRoleRelDO = UserRoleRelDO.builder()
                    .userId(userID)
                    .roleId(RoleConstants.COMMON_USER_ROLE_ID)
                    .createTime(LocalDateTime.now())
                    .updateTime(LocalDateTime.now())
                    .isDeleted(DeleteEnum.NO.getValue())
                    .build();

            userRoleRelDOMapper.insert(userRoleRelDO);

            RoleDO roleDO = roleDOMapper.selectByPrimaryKey(RoleConstants.COMMON_USER_ROLE_ID);

            // 将该用户的角色 ID 存入 Redis 中，指定初始容量为 1，这样可以减少在扩容时的性能开销
            List<String> roles = new ArrayList<>(1);
            roles.add(roleDO.getRoleKey());

            String userRolesKey = RedisKeyConstants.buildUserRoleKey(userID);

            redisTemplate.opsForValue().set(userRolesKey, JsonUtils.toJsonString(roles));

            return userID;
        });
    }
}
